package com.sec.android.app.myfiles.module.local.securelocal;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.SystemClock;
import android.provider.MediaStore;

import com.sec.android.app.myfiles.feature.PrivateModeMgr;
import com.sec.android.app.myfiles.info.AppFeatures;
import com.sec.android.app.myfiles.info.FileType;
import com.sec.android.app.myfiles.info.MediaFile;
import com.sec.android.app.myfiles.info.MimeType;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.provider.DbTableInfo;
import com.sec.android.app.myfiles.util.FileUtils;
import com.sec.android.app.myfiles.util.SemFwWrapper;
import com.sec.android.app.myfiles.util.StorageMonitor;
import com.sec.android.app.myfiles.util.UiUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static com.sec.android.app.myfiles.provider.PrivateMyFilesProvider.DATABASE_NAME;

public class SecureLocalFileRecord extends FileRecord {

    private static final String TAG = "SecureLocalFileRecord";

    public static final int WITH_HIDDEN = 0;
    public static final int WITHOUT_HIDDEN = 1;

    public SecureLocalFileRecord(int id, String path, String name, long size, long date, int fileType, int isHidden, int itemCount, int itemCountHidden) {
        super(StorageType.Local, id, path, name, size, date, fileType, isHidden, itemCount, itemCountHidden);
    }

    public SecureLocalFileRecord(String fullPath) {
        fillDefaultInfoByPath(fullPath, null);
    }

    public SecureLocalFileRecord(String fullPath, Context context) {
        fillDefaultInfoByPath(fullPath, context);
    }

    public SecureLocalFileRecord(String path, Uri uri) {
        fillDefaultInfoByPath(path, null);
        mUri = uri;
    }

    private void fillDefaultInfoByPath(String fullPath, Context context) {
        mStorageType = StorageType.SecureLocal;
        if (fullPath == null) {
            return;
        }
        File file = SemFwWrapper.file(fullPath);
        if (file != null) {
            mPath = file.getParent();
            mName = file.getName();

            if (file.exists()) {
                mSize = file.length();
                mDate = file.lastModified();
                if (file.isDirectory()) {
                    mFileType = FileType.FOLDER;
                } else {
                    if (context != null) {
                        mFileType = MediaFile.getFileType(fullPath, context);
                    } else {
                        mFileType = MediaFile.getFileType(fullPath);
                    }
                }
            } else {
                mSize = 0;
                mDate = 0;
                mFileType = FileType.UNKNOWN;
            }

            mItemCount = -1;
            mItemCountHidden = -1;
        }
    }


    @Override
    public String getDisplayName(Context context) {
        String ret = getName();
        if (isRoot()) {
            ret = StorageMonitor.getDisplayName(context, getFullPath());
        }
        return ret;
    }

    public boolean isRoot() {
        return StorageMonitor.isStorageRoot(getFullPath());
    }

    public static int[] getChildFileCount(File file) {
        int nRet[] = {0, 0};  // index -  0: include hidden files  1: exclude hidden files

        long start = SystemClock.elapsedRealtime();
        if (file.exists()) {
            String names[] = file.list();
            if (names != null) {
                nRet[WITH_HIDDEN] = names.length;
                int countHidden = 0;

                for (String name : names) {
                    if (name.startsWith(".")) {
                        countHidden++;
                    }
                }
                nRet[WITHOUT_HIDDEN] = nRet[WITH_HIDDEN] - countHidden;
            }
        }
        Log.d("myfiles", "getChildFileCount() ] Elapsed time to getChildFileCount = " + (SystemClock.elapsedRealtime() - start) + " , ChildCount = " + nRet[WITH_HIDDEN] + " , without hidden = " + nRet[WITHOUT_HIDDEN]);
        return nRet;

    }

    @Override
    public int getItemCount(boolean showHidden) {
        int ret = super.getItemCount(showHidden);
        if (ret < 0) {
            int childCounts[] = getChildFileCount(SemFwWrapper.file(getFullPath()));

            mItemCountHidden = childCounts[WITH_HIDDEN];
            mItemCount = childCounts[WITHOUT_HIDDEN];

            ret = childCounts[WITHOUT_HIDDEN];
            if (showHidden) {
                ret = childCounts[WITH_HIDDEN];
            }
        }
        return ret;
    }

    @Override
    protected int _getId() {
        return -1;
    }

    @Override
    protected Uri _getUri(Context context) {
        Uri ret = null;

        if (mId == -1) {
            mId = getRealMediaId(context);
        }

        if (mId >= 0 && !isDirectory()) {
            ret = UiUtils.addUserIdToUri(ContentUris.withAppendedId(getMediaProviderUri(context, mId), mId));
        }

        if (ret == null) {
            ret = FileUtils.getContentUri(context, getFullPath());
        }


        return ret;
    }

    private Uri getMediaProviderUri(Context context, int index) {
        Uri ret = null;

        if (!FileUtils.isNoMediaFile(getFullPath())) {
            if (FileType.isImageFileType(mFileType) && !isMediaTypeNone(context, index)) {
                ret = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if (FileType.isAudioFileType(mFileType) && !isMediaTypeNone(context, index)) {
                if (mFileType != FileType.QCP || AppFeatures.SUPPORT_QCP) {
                    ret = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }
            } else if (FileType.isVideoFileType(mFileType) && !isMediaTypeNone(context, index)) {
                ret = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            }
        }

        if (ret != null) {
            if (!isUriContentsExisted(context, ret, index)) {
                ret = null;
            }
        }

        if (ret == null) {
            ret = FileUtils.MEDIA_PROVIDER_URI;
        }

        return ret;
    }

    private boolean isUriContentsExisted(Context context, Uri uri, int index) {
        boolean bRet = false;

        try (Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Files.FileColumns._ID}, MediaStore.Files.FileColumns._ID + "=" + index, null, null)) {
            if (cursor != null && cursor.getCount() > 0) {
                bRet = true;
            }
        } catch (Exception e) {
            Log.e(this, "Exception : " + e.toString());
        }

        return bRet;
    }

    private boolean isMediaTypeNone(Context context, int index) {
        boolean bRet = true;

        try (Cursor cursor = context.getContentResolver().query(FileUtils.MEDIA_PROVIDER_URI, new String[]{MediaStore.Files.FileColumns.MEDIA_TYPE}, MediaStore.Files.FileColumns._ID + "=" + index, null, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                int mediaType = cursor.getInt(0);
                if (mediaType != MediaStore.Files.FileColumns.MEDIA_TYPE_NONE) {
                    bRet = false;
                }
            }
        } catch (Exception e) {
            Log.e(this, "Exception : " + e.toString());
        }

        return bRet;
    }

    @Override
    public boolean exists(Context context) {
        boolean exist;
        int storageType = StorageMonitor.getMatchedStorageType(getFullPath());
        if (storageType == StorageMonitor.iStorageType.EXTERNAL_PRIVATE_MODE) {
            PrivateModeMgr privateModeMgr = PrivateModeMgr.getInstance(context);
            exist = privateModeMgr.isReady();
        } else {
            exist = StorageMonitor.isStorageMounted(context, storageType);
        }
        if (exist) {
            File srcFile = SemFwWrapper.file(getFullPath());
            exist = srcFile.exists();
        }
        return exist;
    }

    public static void updateMediaId(Context context, ArrayList<FileRecord> recordList) {
        HashMap<String, SecureLocalFileRecord> needUpdateList = getNeedUpdateList(recordList);
        if (!needUpdateList.isEmpty()) {
            ContentResolver resolver = context.getContentResolver();
            getMediaId(resolver, needUpdateList);

            String rawStatement = "UPDATE " + "local_files" +
                    " SET " + DbTableInfo.COLUMN_ID.MEDIA_ID + " = CASE " +
                    DbTableInfo.COLUMN_ID.PATH + " || '/' || " + DbTableInfo.COLUMN_ID.NAME + " ";

            StringBuilder casesString = new StringBuilder();
            List<String> params = new ArrayList<>();
            for (SecureLocalFileRecord record : needUpdateList.values()) {
                if (record.mId > 0) {
                    casesString.append(" WHEN ").append("?").append(" THEN ").append(record.mId);
                    params.add(record.getFullPath());
                }
            }
            casesString.append(" ELSE ").append(DbTableInfo.COLUMN_ID.MEDIA_ID).append(" END");

            if (!params.isEmpty()) {
                rawStatement = rawStatement + casesString;

                Object[] param = params.toArray();

                String dbPath = context.getDatabasePath(DATABASE_NAME).toString();
                SQLiteDatabase db = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE);
                db.execSQL(rawStatement, param);
                db.close();
            }

            needUpdateList.clear();
        }
    }

    private static void getMediaId(ContentResolver resolver, HashMap<String, SecureLocalFileRecord> needUpdateList) {
        String selection = getSelection(needUpdateList);
        String[] selectionArgs = getSelectionArgs(needUpdateList);
        String[] projection = new String[]{MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA};


        try (Cursor cursor = resolver.query(FileUtils.MEDIA_PROVIDER_URI, projection, selection, selectionArgs, null)) {
            if (cursor != null && cursor.getCount() > 0) {
                cursor.moveToFirst();
                do {
                    SecureLocalFileRecord record = needUpdateList.get(cursor.getString(1));
                    if (record != null) {
                        record.mId = cursor.getInt(0);
                    }
                } while (cursor.moveToNext());
            }
        }
    }

    public int getRealMediaId(Context context) {
        int mediaId = -1;

        StringBuilder mediaWhere = new StringBuilder();
        mediaWhere.append(MediaStore.Files.FileColumns.DATA).append("=?");
        String[] mediaWhereArgs = new String[]{getFullPath()};
        try (Cursor cursor = context.getContentResolver().query(FileUtils.MEDIA_PROVIDER_URI, new String[]{MediaStore.Files.FileColumns._ID}, mediaWhere.toString(), mediaWhereArgs, null)) {
            DbTableInfo tableInfo = DbTableInfo.getInstance(StorageType.Local);
            if (tableInfo != null && cursor != null && cursor.moveToFirst()) {
                ContentResolver resolver = context.getContentResolver();
                ContentValues value = new ContentValues();
                mediaId = cursor.getInt(0);
                value.put(tableInfo.getColumnName(DbTableInfo.COLUMN_ID.MEDIA_ID), mediaId);
                StringBuilder updateWhere = new StringBuilder();
                updateWhere.append(tableInfo.getColumnName(DbTableInfo.COLUMN_ID.PATH)).append("=? AND ").append(tableInfo.getColumnName(DbTableInfo.COLUMN_ID.NAME)).append("=?");
                String[] updateWhereArgs = new String[]{getPath(), getName()};
                resolver.update(Uri.parse(tableInfo.getUri()), value, updateWhere.toString(), updateWhereArgs);
            }
        } catch (SecurityException e) {
            Log.e(TAG, "SecurityException:" + e.toString());
        }

        return mediaId;
    }

    private static HashMap<String, SecureLocalFileRecord> getNeedUpdateList(ArrayList<FileRecord> recordList) {
        HashMap<String, SecureLocalFileRecord> ret = new HashMap<>();

        for (FileRecord record : recordList) {
            if (record instanceof SecureLocalFileRecord) {
                if (record.getId() < 0) {
                    ret.put(record.getFullPath(), (SecureLocalFileRecord) record);
                }
            }
        }

        return ret;
    }

    private static String[] getSelectionArgs(HashMap<String, SecureLocalFileRecord> recordHashMap) {
        String[] ret = new String[recordHashMap.size()];
        int i = 0;
        for (SecureLocalFileRecord record : recordHashMap.values()) {
            ret[i++] = record.getFullPath();
        }
        return ret;
    }

    private static String getSelection(HashMap<String, SecureLocalFileRecord> recordHashMap) {
        StringBuilder where = new StringBuilder();
        where.append(MediaStore.Files.FileColumns.DATA).append(" IN ( ");
        boolean first = true;

        for (int i = 0; i < recordHashMap.size(); i++) {
            if (first) {
                first = false;
            } else {
                where.append(", ");
            }
            where.append('?');

        }
        where.append(" ) ");

        return where.toString();
    }

    @Override
    public void setMimeType(String mimeType) {
        /**
         * Temporary code for ppt mime type (P160818-02910)
         */
        if (mimeType != null && mimeType.equalsIgnoreCase(MimeType.MSOFFICE_PPT_GOOGLE)) {
            mimeType = MimeType.MSOFFICE_PPT_MS;
        }

        super.setMimeType(mimeType);
    }
}
